Week 7A
Geospatial Data: Street Networks

Show Code
# The usual imports
import altair as alt
import geopandas as gpd
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
Show Code
# Show all columns in dataframes
pd.options.display.max_columns = 999

# Hide warnings due to issue in shapely package 
# See: https://github.com/shapely/shapely/issues/1345
np.seterr(invalid="ignore");

1 Today: Urban street networks and OpenStreetMap (OSM)

  • Two tools that make working with OSM data very easy
  • What kind of questions can we answer?
    • Street orientations
    • Mapping event points to streets: car crashes
    • Mapping amenities
    • Network-constrained distances: accessibility

2 OSM: what is it?

  • Collaborative mapping
  • A free editable map of the World
  • Sort of like Wikipedia for maps

Great source of data: street networks and a wealth of amenity information

https://www.openstreetmap.org

3 Working with OSM data

  • Raw data is very messy
  • Two relatively new, amazing Python packages greatly simply the process
  • Related, but complementary features
    • OSMnx: downloading and manipulating streets as networks
    • Pandana: networks focused on accessibility of amenities

Related: interactive web maps in Python

4 Part 1: OSMnx

Relatively new package (just a few years old), but quickly becoming the go to package for working with street networks and OpenStreetMap data in Python.

Several key features:

Show Code
# !pip install osmnx
Show Code
import osmnx as ox

4.1 1.1. Getting boundary shapefiles from OSM

Key function: geocode_to_gdf() (docs)

We can get the boundary for anything identified as a “place” by OSM.

Important: Be careful to pass the right place name that OSM needs

Show Code
philly = ox.geocode_to_gdf("Philadelphia, PA")

philly.head()
geometry bbox_west bbox_south bbox_east bbox_north place_id osm_type osm_id lat lon class type place_rank importance addresstype name display_name
0 POLYGON ((-75.28027 39.97496, -75.28023 39.974... -75.280266 39.867005 -74.955831 40.137959 331386626 relation 188022 39.952724 -75.163526 boundary administrative 16 0.766493 city Philadelphia Philadelphia, Philadelphia County, Pennsylvani...
Show Code
philly.crs
<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World.
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich

We can plot it just like any other GeoDataFrame

Show Code
# Project it to Web Mercator first and plot
ax = philly.to_crs(epsg=3857).plot(facecolor="none", edgecolor="black")
ax.set_axis_off()

4.2 1.2 Projecting with OSMnx

Key function: project_gdf() (docs)

Automatically projects to the Universal Transverse Mercator (UTM) CRS for the UTM zone that the centroid of the polygon lies in

A good, general projection that works for most latitudes except very northern locations.

Show Code
# ax = ox.project_gdf(philly).plot(fc="lightblue", ec="gray")
# ax.set_axis_off()

import geopandas as gpd

# Reproject philly to its best UTM zone
philly_proj = philly.to_crs(philly.estimate_utm_crs())

# Plot
ax = philly_proj.plot(fc="lightblue", ec="gray")
ax.set_axis_off()

Some more examples:

Show Code
# Some examples
place1 = ox.geocode_to_gdf("Manhattan, New York City, New York, USA")
place2 = ox.geocode_to_gdf("Miami-Dade County, Florida")
place3 = ox.geocode_to_gdf("Florida, USA")
place4 = ox.geocode_to_gdf("Spain")
Show Code
# # Manhattan
# ax = ox.project_gdf(place1).plot(fc="lightblue", ec="gray")
# ax.set_axis_off()

# Reproject philly to its best UTM zone
place1_repoj = place1.to_crs(place1.estimate_utm_crs())
ax = place1_repoj.plot(fc="lightblue", ec="gray")
ax.set_axis_off()

Show Code
# # Miami-Dade County
# ax = ox.project_gdf(place2).plot(fc="lightblue", ec="gray")
# ax.set_axis_off()

# Reproject philly to its best UTM zone
place2_repoj = place2.to_crs(place2.estimate_utm_crs())
ax = place2_repoj.plot(fc="lightblue", ec="gray")
ax.set_axis_off()

Show Code
# # Florida
# ax = ox.project_gdf(place3).plot(fc="lightblue", ec="gray")
# ax.set_axis_off()

# Reproject philly to its best UTM zone
place3_repoj = place3.to_crs(place3.estimate_utm_crs())
ax = place3_repoj.plot(fc="lightblue", ec="gray")
ax.set_axis_off()

Show Code
# # Spain
# ax = ox.project_gdf(place4).plot(fc="lightblue", ec="gray")
# ax.set_axis_off()

# Reproject philly to its best UTM zone
place4_repoj = place4.to_crs(place4.estimate_utm_crs())
ax = place4_repoj.plot(fc="lightblue", ec="gray")
ax.set_axis_off()

4.3 1.3 Downloading OSM features

Key functions: features_from_*

  • features_from_place() (docs)
    • Download features within an OSM place boundary
  • features_from_address() (docs)
    • Download features within a certain distance of an address
  • features_from_bbox() (docs)
    • Download features within a N, S, E, W bounding box
  • features_from_point() (docs)
    • Download features within a certain distance of a specified point
  • features_from_polygon() (docs)
    • Download features within a polygon object
About OSM features

Important reference: https://wiki.openstreetmap.org/wiki/Map_Features

  • OSM uses a tagging system to categorize different map features
  • The main feature categories are available on the OSM Wikipedia
    • Examples: ‘amenity’, ‘building’, ‘landuse’, ‘highway’
  • There are specific sub-categories for each feature type too:
    • Amenity examples: ‘bar’, ‘college’, ‘library’

In the language of OSM, the “key” is the main feature category (e.g., amenity) and the “value” is the sub-category type (e.g., “bar”)

osmnx mirrors the key/value syntax of OSM. Use a dict to specify the features you want:

Show Code
# Get all amenities in Philadelphia
amenities = ox.features_from_place("Philadelphia, PA", tags={"amenity": True})
Show Code
len(amenities)
15646
Show Code
amenities.head()
geometry amenity brand brand:wikidata name operator operator:wikidata operator:wikipedia short_name addr:city addr:housenumber addr:postcode addr:state addr:street air_conditioning branch cuisine official_name opening_hours phone ref takeaway website created_by source alt_name atm contact:facebook diet:vegetarian payment:cash payment:credit_cards payment:debit_cards wheelchair brand:website brand:wikipedia healthcare internet_access url contact:phone description email wikidata brewery contact:instagram diet:vegan education ele gnis:feature_id religion check_date denomination building old_name comment operator:type school wikipedia service_times historic:amenity name:en social_facility operator:short operator:website emergency healthcare:speciality bicycle_parking capacity covered library ref:isil addr:country callsign studio craft microbrewery outdoor_seating drive_through check_date:opening_hours contact:website reservation website:menu addr:unit internet_access:fee shop training toilets:wheelchair contact:linkedin contact:tiktok contact:twitter addr:housename delivery indoor_seating bottle fountain collection_times parking self_service dispensing opening_hours:drive_through opening_hours:signed screen payment:coins smoking architect artist_name wikimedia_commons female fee check_date:opening_hours:drive_through contact:myspace fax bar designation tourism source:pkey fixme access changing_table toilets name:zh addr:full mobile charge panoramax fuel:biodiesel fuel:biogas fuel:cng fuel:diesel fuel:e10 fuel:e85 fuel:electricity sport collection_times:signed ref:signed drive_in compressed_air check_date:collection_times food cash_in indoor toilets:access not:brand:wikidata diet:halal opening_hours:atm payment:apple_pay payment:contactless toilets:handwashing toilets:handwashing:soap check_date:diet:vegan diet:organic diet:meat leisure drink:club-mate car_wash note bicycle_rental kiosk min_age network network:wikidata owner owner:wikidata ownership payment:app rental sponsor sponsor:wikidata toilets:disposal opening_hours:bar opening_hours:kitchen diet:pescetarian lgbtq park_ride supervised power_supply addr:place flickr was:shop contact:email facebook instagram twitter youtube source:cuisine disused:shop toilets:position male maxheight wheelchair:description wheelchair:description:en internet_access:ssid layer service:bicycle:chain_tool service:bicycle:pump currency:USD drink:coca-cola drink:coke drink:cola drink:cola_zero drink:soda drink:water vending network:wikipedia amenity_1 recycling:computers recycling:tv_monitor recycling_type ferry public_transport entrance toilets:unisex kitchen_hours animal fixme:atp website:orders website:reservation check_date:wheelchair backrest street_vendor service:bicycle:repair payment:cards name:es diet:kosher social_facility:for theatre:genre beds level payment:visa was:name happy_hours opening_hours:brunch opening_hours:dinner opening_hours:lunch direction office date display faces armrest seats material name:ca addr:city:ar name:ar service:bicycle:tools colour brand:type parcel_locker:type parcel_mail_in parcel_pickup waste historic artwork_type start_date support type payment:american_express payment:discover_card payment:mastercard dog recycling:beverage_cartons recycling:cans recycling:glass_bottles recycling:paper recycling:plastic recycling:plastic_bottles recycling:plastic_packaging contact:yelp portable seasonal unisex internet_access:description man_made particle_accelerator substance payment:cheque heritage clock inscription inscription:1 panoramax:1 bus tram opening_hours:bottle_shop barrier public_bookcase:type check_date:capacity payment:cash_app payment:venmo seating currency:XBT theatre:type smoothness surface opening_hours:Condesa opening_hours:El_Cafe opening_hours:El_Techo shelter_type currency:BCH manufacturer payment:onchain cash_out brand:en brand:zh location lit drinking_water height fridge opening_hours:covid19 diet:healthy abandoned:amenity artwork_subject memorial subject:wikidata payment:lightning payment:lightning_contactless delivery:partner internet_access:password post_box:type check_date:diet:vegetarian description:en check_date:currency:XBT check_date:smoking phone:mnemonic addr:door was:amenity urgent_care community_centre:for community_centre animal_shelter cash name:etymology name:etymology:wikidata image operator:short_name automated social_centre:for cash_withdrawal nobrand recycling:clothes animal_training parking_space bike_ride bin polling_station shop:music:vinyl books manufacturer:wikidata model model:ref addr:floor diet survey:date former:shop cafe contact:bluesky website:booking fuel nursery preschool diet:gluten_free int_name old_ref mimics fast_food gender_segregated heating live_music was:addr:housenumber healthcare:for tactile_paving animal_boarding recycling:waste authentication:app bicycle frequency hgv maxstay motorcar network:website payment:app:tesla ref:supercharge_info scooter socket:nacs socket:nacs:output diet:mediterranean landuse restaurant brand:short building:levels capacity:disabled building:floor ref:nrhp building:material roof:levels roof:shape vehicle natural water building:use lanes ship:type wifi orientation name:he source:name basilica diocese website:en roof:material access:conditional rite heritage:operator building:architecture heritage:website contact:mastodon architect:wikidata building:levels:underground nrhp:criteria nrhp:inscription_date building:colour ref:dove check_date:compressed_air nonsquare name:ja name:zn roof:orientation demolished:building historic:name grades farm_boxes disused:amenity disused:name post_office building:part monastery:type theme bench loc_name drink subdenomination police was:cuisine was:delivery was:drive_through was:outdoor_seating was:takeaway school:type old_name1 old_name2 elevation fuel:octane_95 fuel:gasoline fuel:octane_87 healthcare:counselling parking:orientation area protection_title source_ref country house diet:lacto_vegetarian name:vi isced:level pedagogy school:gender statue crossing:markings
element id
node 109811385 POINT (-75.19487 40.05846) bench NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
110237774 POINT (-75.15662 39.9504) parking_entrance NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
274216093 POINT (-75.19125 39.95764) atm Citibank Q857063 Citibank Citibank Q857063 en:Citibank Citi NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
274217596 POINT (-75.19229 39.95301) cafe Starbucks Q37158 Starbucks NaN NaN NaN NaN Philadelphia 3401 19104 PA Walnut Street yes Walnut & 34th coffee_shop Starbucks Coffee Mo-Fr 05:30-20:00; Sa,Su 06:00-20:00 +1-215-387-1914 14716 yes https://www.starbucks.com/store-locator/store/... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
313440966 POINT (-75.16694 39.96394) car_sharing NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN Potlatch 0.10f local_knowledge NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
Show Code
# Get all bars in philadelphia
bars = ox.features_from_place("Philadelphia, PA", tags={"amenity": "bar"})
Show Code
len(bars)
166
Show Code
bars.head()
geometry addr:housenumber addr:street amenity contact:phone contact:website description name opening_hours outdoor_seating check_date contact:facebook addr:postcode phone website addr:city air_conditioning smoking toilets:wheelchair wheelchair check_date:opening_hours opening_hours:signed indoor_seating contact:instagram leisure sport lgbtq access brand brand:wikidata wheelchair:description opening_hours:kitchen addr:state branch cuisine fixme name:en name:es addr:unit email craft heritage wikidata wikipedia brewery min_age opening_hours:bottle_shop operator alt_name height source internet_access website:menu landuse microbrewery restaurant atm building building:levels food nonsquare shop contact:tiktok contact:twitter level designation
element id
node 1033935437 POINT (-75.17552 39.95242) 2101 Chestnut Street bar +1 215-568-3355 https://mixbarandgrillphilly.com/location/mix-... The bar is open until whenever the rush ends. Mix Bar & Grill Mo-We,Su 11:00-22:00; Th-Sa 11:00-23:00 yes NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1167079387 POINT (-75.14925 39.9417) 401 South Street bar +1 215-925-6455 https://www.milkboy.tv/southstreet NaN MilkBoy South Street Mo-Tu,Su 11:00-24:00; We-Sa 11:00-25:00 NaN 2023-04-12 https://www.facebook.com/milkboysouthstreet NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1204761128 POINT (-75.16363 39.92685) NaN NaN bar NaN NaN NaN Vikings High School Club NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1424271155 POINT (-75.14909 39.94152) 344 South Street bar NaN NaN NaN Copabanana Mo-Su 11:30-02:00 NaN NaN NaN 19147 +1 215-923-6180 https://copabanana.com/ NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1475230378 POINT (-75.14002 39.96627) 1001 North 2nd Street bar NaN NaN NaN Gunner's Run Mo-Su 11:00-02:00 NaN NaN NaN 19123 +1 215-923-4600 http://gunnersrun.com/ Philadelphia NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
Show Code
# Get bar, pub, and restaurant features in Philadelphia
food_and_drink = ox.features_from_place(
    "Philadelphia, PA", tags={"amenity": ["pub", "bar", "restaurant"]}
)
Show Code
len(food_and_drink)
1148
Show Code
food_and_drink.head()
geometry addr:city addr:housenumber addr:postcode addr:state addr:street alt_name amenity atm contact:facebook cuisine diet:vegetarian name opening_hours payment:cash payment:credit_cards payment:debit_cards source wheelchair brewery contact:instagram diet:vegan email phone website check_date craft microbrewery outdoor_seating brand brand:wikidata check_date:opening_hours contact:website reservation website:menu contact:phone contact:twitter internet_access self_service indoor_seating opening_hours:signed description wikimedia_commons air_conditioning smoking bar designation takeaway addr:housename toilets:wheelchair short_name name:zh sport delivery drive_in food not:brand:wikidata toilets diet:halal addr:country drive_through branch official_name diet:organic diet:meat leisure opening_hours:bar opening_hours:kitchen operator fixme diet:pescetarian lgbtq source:cuisine access wheelchair:description facebook ref amenity_1 capacity fax kitchen_hours wikidata website:orders website:reservation name:en street_vendor addr:unit name:es brand:wikipedia was:name level note happy_hours opening_hours:brunch opening_hours:dinner opening_hours:lunch payment:apple_pay payment:cards payment:contactless name:ca internet_access:fee payment:american_express payment:discover_card payment:mastercard payment:visa start_date contact:yelp internet_access:description internet_access:ssid heritage wikipedia min_age opening_hours:bottle_shop opening_hours:Condesa opening_hours:El_Cafe opening_hours:El_Techo diet:healthy delivery:partner toilets:access height check_date:smoking panoramax phone:mnemonic diet:gluten_free was:addr:housenumber diet:mediterranean toilets:unisex landuse restaurant building historic ship:type source:name website:en building:levels contact:mastodon image roof:levels name:ja name:zn nonsquare shop theme building:colour building:material roof:shape contact:tiktok house
element id
node 333786044 POINT (-75.15893 39.94086) Philadelphia 735 19147 PA South 10th Street Morning Glory Diner restaurant yes https://www.facebook.com/MorningGloryDiner/ diner yes Sam's Morning Glory Mo-Fr 08:00-14:00; Sa,Su 08:00-15:00 yes no no knowledge;survey limited NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
343293806 POINT (-75.15946 39.96073) Philadelphia 501 19123 PA North 13th Street NaN pub NaN NaN american;burger;fish_and_chips;sandwich yes Prohibition Taproom Fr-Sa 12:00-01:00; Su 12:00-00:00; Mo-Th 16:00... NaN NaN NaN survey NaN yes prohibition_taproom yes info@theprohibitiontaproom.com 215-238-1818 http://theprohibitiontaproom.com/ NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
566683522 POINT (-75.15755 39.96205) Philadelphia 1139 19123 NaN Spring Garden Street NaN restaurant NaN NaN pizza NaN Spring Garden Pizza & Restaurant Mo-Fr 08:00-20:00; Sa 08:00-17:00; Su off NaN NaN NaN NaN NaN NaN NaN NaN NaN +1 215-765-7665 https://www.springgardenpizza.com 2023-10-11 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
596230881 POINT (-75.18682 40.05492) NaN NaN NaN NaN NaN NaN restaurant NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
596245658 POINT (-75.19035 40.0592) Philadelphia 7136 19119 PA Germantown Avenue NaN restaurant NaN NaN NaN NaN Earth Bread & Brewery NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN brewery yes NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
Show Code
# Get bus stop features
bus_stops = ox.features_from_place("Philadelphia, PA", tags={"highway": "bus_stop"})
Show Code
len(bus_stops)
324
Show Code
bus_stops.head()
geometry bench bus covered highway name network network:wikidata operator public_transport ref shelter network:wikipedia tactile_paving wheelchair route_ref local_ref bin lit designation source check_date:shelter departures_board internet_access addr:street route_ref_1 operator:wikidata description note brand brand:wikidata railway tram trolleybus network:short not:network:wikidata advertising addr:city addr:housenumber addr:postcode addr:state ref:signed fixme
element id
node 361011456 POINT (-75.16166 39.95223) yes yes yes bus_stop 13th St & Market St SEPTA Q2037863 SEPTA platform 10264 yes NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
750281693 POINT (-75.07732 40.01797) NaN yes NaN bus_stop NaN NaN NaN NaN platform NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
768271130 POINT (-75.20726 40.01487) NaN yes NaN bus_stop Wissahickon Transportation Center SEPTA Q2037863 SEPTA platform NaN yes NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1015342921 POINT (-75.18187 39.9664) NaN yes NaN bus_stop NaN NaN NaN NaN platform NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1015342926 POINT (-75.18024 39.9667) NaN yes NaN bus_stop NaN NaN NaN NaN platform NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
Show Code
# Get commercial and retail landuse features
landuse = ox.features_from_place(
    "Philadelphia, PA", tags={"landuse": ["commercial", "retail"]}
)
Show Code
len(landuse)
349

Let’s plot the land use features:

Show Code
from matplotlib import pyplot as plt

fig, ax = plt.subplots(figsize=(10, 6))

ax = landuse.plot(ax=ax)
ax.set_axis_off()

4.4 1.4 Downloading street networks

Key functions: graph_from_*

  • graph_from_place() (docs)
    • Download street network within an OSM place boundary
  • graph_from_address() (docs)
    • Download street network within a certain distance of an address
  • graph_from_bbox() (docs)
    • Download street network within a N, S, E, W bounding box
  • graph_from_point() (docs)
    • Download street network within a certain distance of a specified point
  • graph_from_polygon() (docs)
    • Download street network within a polygon object

4.4.1 Street network around an address

Get streets within 500 meters of the center of Center City

Show Code
G = ox.graph_from_address("Center City, Philadelphia, PA", dist=500)

Project and plot it:

Show Code
G_projected = ox.project_graph(G)
ox.plot_graph(G_projected);

Remove the nodes:

Show Code
ox.plot_graph(G_projected, node_size=0);

Let’s zoom out to 2,000 meters. This will take a little longer.

Show Code
G = ox.graph_from_address("Center City, Philadelphia, PA", dist=2000)
G_projected = ox.project_graph(G)
Show Code
ox.plot_graph(G_projected, node_size=0);

4.4.2 Getting different network types

  • drive - get drivable public streets (but not service roads)
  • drive_service - get drivable streets, including service roads
  • walk - get all streets and paths that pedestrians can use (this network type ignores one-way directionality)
  • bike - get all streets and paths that cyclists can use
  • all - download all non-private OSM streets and paths
  • all_private - download all OSM streets and paths, including private-access ones (default)
Show Code
# the "drive" network
G = ox.graph_from_address("Center City, Philadelphia, PA", network_type="drive", dist=2000)
ox.plot_graph(G);

Show Code
# the "walk" network
G = ox.graph_from_address("Center City, Philadelphia, PA", network_type="walk", dist=2000)
ox.plot_graph(ox.project_graph(G));

4.4.3 Street network within a place boundary

Use graph_from_place() to get the streets within a specific OSM place.

Caution

A couple of things to watch out for: 1. The place query has to be resolved by OSM. 2. If the place is a big city, you might be downloading a lot of data!

Let’s check out the street network for Berkeley, CA:

Show Code
berkeley = ox.graph_from_place("Berkeley, California, USA")
Show Code
ox.plot_graph(ox.project_graph(berkeley), node_size=0);

4.4.4 Streets within a specific polygon

Example: Let’s download all streets within Center City. We will use the “Central” planning district as a proxy for this area. The city’s planning districts are available on Open Data Philly.

Show Code
planning_districts = gpd.read_file(
    "https://opendata.arcgis.com/datasets/0960ea0f38f44146bb562f2b212075aa_0.geojson"
)
Show Code
len(planning_districts)
18
Show Code
planning_districts.head()
objectid dist_name abbrev Shape__Area Shape__Length geometry
0 1 River Wards RW 3.338947e+07 26635.524113 POLYGON ((-75.09798 40.00496, -75.09687 40.005...
1 2 North Delaware NDEL 4.285306e+07 35525.335312 POLYGON ((-74.98159 40.05363, -74.98139 40.053...
2 3 Lower Far Northeast LFNE 4.875252e+07 36958.071333 POLYGON ((-74.96443 40.11728, -74.96434 40.117...
3 4 Central CTR 2.822133e+07 28401.823065 POLYGON ((-75.14791 39.96733, -75.14715 39.967...
4 5 University Southwest USW 2.051763e+07 25957.938149 POLYGON ((-75.18742 39.96338, -75.18644 39.963...

Trim to the central district:

Show Code
central_district = planning_districts.query("dist_name == 'Central'")

And plot it:

Show Code
## old version previous OSMnx 2.0
# ax = ox.project_gdf(central_district).plot(fc="lightblue", ec="gray")
# ax.set_axis_off()

# Reproject philly to its best UTM zone
central_district_repoj = central_district.to_crs(central_district.estimate_utm_crs())
ax = central_district_repoj.plot(fc="lightblue", ec="gray")
ax.set_axis_off()

Show Code
central_district, central_district_repoj.crs
(   objectid dist_name abbrev   Shape__Area  Shape__Length  \
 3         4   Central    CTR  2.822133e+07   28401.823065   
 
                                             geometry  
 3  POLYGON ((-75.14791 39.96733, -75.14715 39.967...  ,
 <Projected CRS: EPSG:32618>
 Name: WGS 84 / UTM zone 18N
 Axis Info [cartesian]:
 - E[east]: Easting (metre)
 - N[north]: Northing (metre)
 Area of Use:
 - name: Between 78°W and 72°W, northern hemisphere between equator and 84°N, onshore and offshore. Bahamas. Canada - Nunavut; Ontario; Quebec. Colombia. Cuba. Ecuador. Greenland. Haiti. Jamaica. Panama. Turks and Caicos Islands. United States (USA). Venezuela.
 - bounds: (-78.0, 0.0, -72.0, 84.0)
 Coordinate Operation:
 - name: UTM zone 18N
 - method: Transverse Mercator
 Datum: World Geodetic System 1984 ensemble
 - Ellipsoid: WGS 84
 - Prime Meridian: Greenwich)

Squeeze it so we can get the geometry polygon:

Show Code
center_city_outline = central_district.squeeze().geometry

center_city_outline, type(center_city_outline)
(<POLYGON ((-75.148 39.967, -75.147 39.967, -75.147 39.969, -75.147 39.97, -7...>,
 shapely.geometry.polygon.Polygon)

Now, let’s use ox.graph_from_polygon() to extract streets within this polygon.

Show Code
# Get the graph
G_cc = ox.graph_from_polygon(center_city_outline, network_type="drive")
Show Code
# Viola!
ox.plot_graph(ox.project_graph(G_cc), node_size=0);

4.5 1.5 Converting from a graph to a GeoDataFrame

Key function: ox.graph_to_gdfs() (docs)

You can get a GeoDataFrame for both the nodes (points) and edges (lines)

Show Code
type(G_cc)
networkx.classes.multidigraph.MultiDiGraph
Show Code
# only get the edges
cc_edges = ox.graph_to_gdfs(G_cc, edges=True, nodes=False)
Show Code
# we have lots of data associated with each edge!
cc_edges.head()
osmid highway lanes name oneway reversed length geometry maxspeed ref bridge tunnel width service access junction
u v key
109727439 109911666 0 132508434 residential 1 Bainbridge Street True False 44.347013 LINESTRING (-75.17104 39.94345, -75.1706 39.94... NaN NaN NaN NaN NaN NaN NaN NaN
109727448 109727439 0 12109011 residential NaN South Colorado Street True False 109.496448 LINESTRING (-75.17125 39.94248, -75.1712 39.94... NaN NaN NaN NaN NaN NaN NaN NaN
110034229 0 12159387 residential NaN Fitzwater Street True False 91.354184 LINESTRING (-75.17125 39.94248, -75.17129 39.9... NaN NaN NaN NaN NaN NaN NaN NaN
109727507 110024052 0 193364514 residential NaN Carpenter Street True False 53.208442 LINESTRING (-75.17196 39.93974, -75.17134 39.9... NaN NaN NaN NaN NaN NaN NaN NaN
109728761 110274344 0 672312336 residential NaN Brown Street True False 58.270358 LINESTRING (-75.17317 39.96951, -75.1725 39.96... 25 mph NaN NaN NaN NaN NaN NaN NaN
Show Code
# plot it like any old GeoDataFrame
ax = cc_edges.to_crs(epsg=3857).plot(color="gray")

# add the neighborhood boundaries
boundary = gpd.GeoSeries([center_city_outline], crs="EPSG:4326")
boundary.to_crs(epsg=3857).plot(
    ax=ax, facecolor="none", edgecolor="red", linewidth=3, zorder=2
)

ax.set_axis_off()

Don’t forget: We can use the .explore() function to get an interactive web map via Folium:

Show Code
cc_edges.explore(tiles='cartodb positron')
Make this Notebook Trusted to load map: File -> Trust Notebook

4.6 1.6 What can we do with the graph?

  • Network-based statistics
  • Routing
  • Street orientations
  • Visualizing crashes

And much more: see the OSMnx repository of Jupyter notebook examples

4.6.1 Street orientations

A great blog post on street orientations across different US and global cities.

One of my favorite examples of data viz, using matplotlib for radial histograms of street orientations…

The code for these histograms is available in the osmnx examples repository.

4.6.2 Network statistics

We can use the ox.basic_stats() to get some basic network statistics

Show Code
ox.basic_stats(G_cc)
{'n': 2103,
 'm': 4041,
 'k_avg': 3.843081312410842,
 'edge_length_total': 357877.4854847953,
 'edge_length_avg': 88.56161481930098,
 'streets_per_node_avg': 3.4070375653827867,
 'streets_per_node_counts': {0: 0, 1: 60, 2: 13, 3: 1061, 4: 952, 5: 14, 6: 3},
 'streets_per_node_proportions': {0: 0.0,
  1: 0.028530670470756064,
  2: 0.006181645268663814,
  3: 0.5045173561578697,
  4: 0.4526866381359962,
  5: 0.006657156443176415,
  6: 0.0014265335235378032},
 'intersection_count': 2043,
 'street_length_total': 311543.5997247771,
 'street_segment_count': 3495,
 'street_length_avg': 89.13979963512935,
 'circuity_avg': 1.0170222084715144,
 'self_loop_proportion': 0.0005722460658082976}

4.6.3 Finding the shortest route

Let’s calculate the shortest route between the Art Museum and the Liberty Bell.

We’ll need to download all amenities in Philadelphia of type “tourism”.

  • The ox.features_from_place() can download OSM features with a specific tag
  • You can consult the OSM pages (the Art Museum and Liberty Bell) for each feature for additional info
  • Both features are categorized as “tourism” in the OSM data — use the “tags” keyword to select this category
Show Code
philly_tourism = ox.features_from_place("Philadelphia, PA", tags={"tourism": True})
Show Code
len(philly_tourism)
892
Show Code
philly_tourism.head()
geometry ele gnis:feature_id name tourism addr:city addr:housenumber addr:postcode addr:state addr:street ... area subject tower:construction tower:type area:highway step_count wikimedia_commons type roof:colour place
element id
node 357371322 POINT (-75.1958 39.9697) 17 2347097 Bird Lake Picnic Area picnic_site NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
360500844 POINT (-75.19582 39.95352) NaN NaN Hilton Inn at Penn hotel Philadelphia 3600 19104 PA Sansom Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
360515304 POINT (-75.1486 39.94771) NaN NaN Robert Morris artwork NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
360542779 POINT (-75.18932 39.9554) NaN NaN Mario the Magnificent artwork NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
360777728 POINT (-75.19021 39.9523) NaN NaN Pennsylvania Historical Marker: ENIAC, first a... information NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 190 columns

Show Code
# How to find the name of the POI: search for keywords
philly_tourism.loc[philly_tourism['name'].str.contains("Art", na=False)]
geometry ele gnis:feature_id name tourism addr:city addr:housenumber addr:postcode addr:state addr:street ... area subject tower:construction tower:type area:highway step_count wikimedia_commons type roof:colour place
element id
node 367974278 POINT (-75.1524 40.03748) 61 2349240 La Salle University Art Museum museum Philadelphia 1900 NaN PA West Olney Avenue ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4064343335 POINT (-75.22113 40.02427) NaN NaN Soft Illusions Fine Art Gallery gallery Philadelphia 4226 19127 PA Main Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5368454121 POINT (-75.19477 39.95406) NaN NaN Institute of Contemporary Art museum Philadelphia 118 19104 PA South 36th Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5718906477 POINT (-75.13058 39.99573) NaN NaN Art Making Machine Studios gallery Philadelphia 3000 19133 PA Hope Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
6639391809 POINT (-75.16088 39.96383) NaN NaN Philadelphia Museum of Jewish Art museum Philadelphia 615 19123 PA North Broad Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
6680708848 POINT (-75.16833 39.94733) NaN NaN Romanian Folk Art Museum museum Philadelphia 1606 19103 PA Spruce Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
12188327106 POINT (-75.14215 39.95191) NaN NaN Arch Enemy Arts gallery Philadelphia 109 19106 PA Arch Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
12193292355 POINT (-75.14336 39.9522) NaN NaN Twelve Gates Arts gallery Philadelphia 106 19106 PA North 2nd Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
12222614599 POINT (-75.14334 39.95113) NaN NaN Larry Becker Contemporary Art gallery Philadelphia 43 19106 PA North 2nd Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
12235452452 POINT (-75.14486 39.95339) NaN NaN Museum for Art in Wood museum Philadelphia 141 19106 PA North 3rd Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
way 28533972 POLYGON ((-75.18117 39.96468, -75.18141 39.964... 32 NaN Philadelphia Museum of Art museum Philadelphia 2600 19130 PA Benjamin Franklin Parkway ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
333931830 POLYGON ((-75.1641 39.95543, -75.16416 39.9551... 13 1196718 Pennsylvania Academy of the Fine Arts museum Philadelphia 118 19102 PA North Broad Street ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
988345364 POLYGON ((-75.21926 40.0832, -75.21931 40.0832... 84 NaN Woodmere Art Museum museum Philadelphia 9201 19118 PA Germantown Avenue ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

13 rows × 190 columns

Show Code
art_museum = philly_tourism.query("name == 'Philadelphia Museum of Art'").squeeze()

art_museum.geometry

Show Code
liberty_bell = philly_tourism.query("name == 'Liberty Bell'").squeeze()

liberty_bell.geometry

Now, extract the lat and lng coordinates

For the Art Museum geometry, we can use the .geometry.centroid attribute to calculate the centroid of the building footprint.

Show Code
liberty_bell_x = liberty_bell.geometry.x
liberty_bell_y = liberty_bell.geometry.y
Show Code
art_museum_x = art_museum.geometry.centroid.x
art_museum_y = art_museum.geometry.centroid.y

Find the nearest nodes on our OSMnx graph, using the ox.nearest_nodes() function

Show Code
ox.nearest_nodes?
Signature: ox.nearest_nodes(G, X, Y, return_dist=False)
Docstring:
Find the nearest node to a point or to each of several points.
If `X` and `Y` are single coordinate values, this will return the nearest
node to that point. If `X` and `Y` are lists of coordinate values, this
will return the nearest node to each point.
If the graph is projected, this uses a k-d tree for euclidean nearest
neighbor search, which requires that scipy is installed as an optional
dependency. If it is unprojected, this uses a ball tree for haversine
nearest neighbor search, which requires that scikit-learn is installed as
an optional dependency.
Parameters
----------
G : networkx.MultiDiGraph
    graph in which to find nearest nodes
X : float or list
    points' x (longitude) coordinates, in same CRS/units as graph and
    containing no nulls
Y : float or list
    points' y (latitude) coordinates, in same CRS/units as graph and
    containing no nulls
return_dist : bool
    optionally also return distance between points and nearest nodes
Returns
-------
nn or (nn, dist) : int/list or tuple
    nearest node IDs or optionally a tuple where `dist` contains distances
    between the points and their nearest nodes
File:      ~/mambaforge/envs/musa-550-fall-2023/lib/python3.10/site-packages/osmnx/distance.py
Type:      function
Show Code
# Get the origin node
orig_node = ox.nearest_nodes(G_cc, liberty_bell_x, liberty_bell_y) 

# Get the destination node
dest_node = ox.nearest_nodes(G_cc, art_museum_x, art_museum_y)

Now, we can use ox.shortest_path() function to find the shortest path between these graph nodes.

Note

We need to specify weight=‘length’ to find the shortest path by minimizing distance traveled (otherwise it treats each edge as weight=1).

Show Code
# get the shortest path --> just a list of node IDs
route1 = ox.shortest_path(G_cc, orig_node, dest_node, weight="length")

route1
[3408446156,
 110217312,
 3425012161,
 109814427,
 5372039288,
 109814445,
 109814449,
 5358069713,
 5372059864,
 2644235371,
 5534891499,
 110540235,
 109750941,
 110402209,
 110402230,
 534960802,
 110391250,
 637791907,
 637791980,
 110329803,
 110329806,
 775897224,
 3414249481,
 110329822,
 110391452,
 110353061,
 11162290432,
 110054089,
 775897147,
 110329848,
 110329851,
 110125895,
 534969088,
 109745905,
 109740423,
 110330569,
 550175399]

Use ox.plot_graph_route() to plot a graph and highlight a specific route

Show Code
ox.plot_graph_route(G_cc, route1, node_size=0);

How about an interactive version?

osmnx has a helper function ox.utils_graph.route_to_gdf() to convert a route to a GeoDataFrame of edges.

Show Code
# ox.utils_graph.route_to_gdf(G_cc, route1, weight="length").explore(
#     tiles="cartodb positron",
#     color="red",
# )

## new OSMnx 2.0 version
import osmnx as ox

# New import location
from osmnx import routing
# Convert route to GeoDataFrame
gdf_route = routing.route_to_gdf(G_cc, route1, weight="length")

# Explore with folium/leafmap
gdf_route.explore(
    tiles="cartodb positron",
    color="red"
)
Make this Notebook Trusted to load map: File -> Trust Notebook

4.6.4 Finding the shortest travel time

The add_edge_speeds function add edge speeds (km per hour) to graph as new speed_kph edge attributes.

The function will add free-flow travel speeds for all edges based on the average maxspeed value of edges, per highway type. This process can definitely be imprecise, but in the case that you happen to have more precise speed data, you can supply your own data instead of relying on OSM data.

Show Code
# impute speed on all edges missing data
G_cc = ox.add_edge_speeds(G_cc)

# calculate travel time (seconds) for all edges
G_cc = ox.add_edge_travel_times(G_cc)
Show Code
# Get the graph edges
edges = ox.graph_to_gdfs(G_cc, nodes=False)
Show Code
edges.head()
osmid highway lanes name oneway reversed length geometry speed_kph travel_time maxspeed ref bridge tunnel width service access junction
u v key
109727439 109911666 0 132508434 residential 1 Bainbridge Street True False 44.347013 LINESTRING (-75.17104 39.94345, -75.1706 39.94... 38.501215 4.146603 NaN NaN NaN NaN NaN NaN NaN NaN
109727448 109727439 0 12109011 residential NaN South Colorado Street True False 109.496448 LINESTRING (-75.17125 39.94248, -75.1712 39.94... 38.501215 10.238306 NaN NaN NaN NaN NaN NaN NaN NaN
110034229 0 12159387 residential NaN Fitzwater Street True False 91.354184 LINESTRING (-75.17125 39.94248, -75.17129 39.9... 38.501215 8.541940 NaN NaN NaN NaN NaN NaN NaN NaN
109727507 110024052 0 193364514 residential NaN Carpenter Street True False 53.208442 LINESTRING (-75.17196 39.93974, -75.17134 39.9... 38.501215 4.975178 NaN NaN NaN NaN NaN NaN NaN NaN
109728761 110274344 0 672312336 residential NaN Brown Street True False 58.270358 LINESTRING (-75.17317 39.96951, -75.1725 39.96... 40.233500 5.213896 25 mph NaN NaN NaN NaN NaN NaN NaN
Show Code
# Groupby highway type and get the mean values
edges["highway"] = edges["highway"].astype(str)
edges.groupby("highway")[["length", "speed_kph", "travel_time"]].mean()
length speed_kph travel_time
highway
['residential', 'unclassified'] 258.436611 40.233500 23.124307
['tertiary', 'motorway_link'] 151.598640 40.233500 13.564694
living_street 52.876864 46.222619 4.118259
motorway 432.116867 81.085977 18.662521
motorway_link 306.735864 46.222619 23.889800
primary 102.944142 43.970544 8.436790
primary_link 55.344736 40.233500 4.952118
residential 80.858445 38.499816 7.586837
secondary 93.071002 42.532557 8.026738
secondary_link 57.032287 40.233500 5.103116
tertiary 77.746546 41.352463 6.787090
tertiary_link 39.973430 40.233500 3.576730
trunk 69.430842 56.326900 4.437507
trunk_link 179.573840 46.222619 13.985919
unclassified 114.078736 37.757592 11.042156
Show Code
# calculate two routes by minimizing travel time
route2 = ox.shortest_path(G_cc, orig_node, dest_node, weight="travel_time")
Show Code
# plot the routes
fig, ax = ox.plot_graph_routes(
    G_cc,
    routes=[route1, route2],
    route_colors=["r", "y"],
    route_linewidth=6,
    node_size=0,
)

Let’s plot both in an interactive map:

Show Code
# m = ox.utils_graph.route_to_gdf(G_cc, route1, weight="length").explore(
#     tiles="cartodb dark matter",
#     color="red",
# )

# m = ox.utils_graph.route_to_gdf(G_cc, route2, weight="length").explore(
#     tiles="cartodb dark matter",
#     color="yellow",
#     m=m
# )


from osmnx import routing  # new location for route_to_gdf

# First route
gdf_route1 = routing.route_to_gdf(G_cc, route1, weight="length")
m = gdf_route1.explore(
    tiles="cartodb dark_matter",
    color="red"
)

# Second route on same map
gdf_route2 = routing.route_to_gdf(G_cc, route2, weight="length")
m = gdf_route2.explore(
    tiles="cartodb dark_matter",
    color="yellow",
    m=m
)

m
Make this Notebook Trusted to load map: File -> Trust Notebook

Let’s compare to the two routes:

Distances:

Show Code
# meters_to_miles = 0.0006213712

# # compare the two routes
# route1_length = (
#     ox.utils_graph.route_to_gdf(G_cc, route1, weight="length")["length"].sum()
#     * meters_to_miles
# )

# route2_length = (
#     ox.utils_graph.route_to_gdf(G_cc, route2, weight="length")["length"].sum()
#     * meters_to_miles
# )

import osmnx as ox
from osmnx import routing  # new module for route_to_gdf

meters_to_miles = 0.0006213712

# Compare the two routes
route1_length = (
    routing.route_to_gdf(G_cc, route1, weight="length")["length"].sum()
    * meters_to_miles
)

route2_length = (
    routing.route_to_gdf(G_cc, route2, weight="length")["length"].sum()
    * meters_to_miles
)

print(f"Route 1 length: {route1_length:.2f} miles")
print(f"Route 2 length: {route2_length:.2f} miles")
Route 1 length: 2.19 miles
Route 2 length: 2.46 miles

Travel times:

Show Code
seconds_to_mins = 1.0 / 60

# route1_time = (
#     ox.utils_graph.route_to_gdf(G_cc, route1, weight="travel_time")["travel_time"].sum()
#     * seconds_to_mins
# )
# route2_time = (
#     ox.utils_graph.route_to_gdf(G_cc, route2, weight="travel_time")["travel_time"].sum()
#     * seconds_to_mins
# )

route1_time = (
    routing.route_to_gdf(G_cc, route1, weight="travel_time")["travel_time"].sum()
    * seconds_to_mins
)
route2_time = (
    routing.route_to_gdf(G_cc, route2, weight="travel_time")["travel_time"].sum()
    * seconds_to_mins
)

Summarize:

Show Code
print(f"Route 1 is {route1_length:.2f} miles and takes {route1_time:.2f} minutes.")
print(f"Route 2 is {route2_length:.2f} miles and takes {route2_time:.2f} minutes.")
Route 1 is 2.19 miles and takes 5.18 minutes.
Route 2 is 2.46 miles and takes 4.57 minutes.

5 Part 2: Pandana

“Pandas Network Analysis - dataframes of network queries, quickly”

A complementary set of OSM-related features: - Downloading OSM-based networks - Extracting amenity data (so-called “Points of Interest”) - Calculating network-constrained distances

Show Code
# !pip install pandana
Show Code
import pandana as pnda
from pandana.loaders import osm

5.1 Step 1: Get amenity data

Key function: osm.node_query()

  • This will extract amenities within a given bounding box.
  • Similar to the ox.features_from_bbox() function in OSMnx, but we slightly different syntax.
Show Code
osm.node_query?
Signature: osm.node_query(lat_min, lng_min, lat_max, lng_max, tags=None)
Docstring:
Search for OSM nodes within a bounding box that match given tags.
Parameters
----------
lat_min, lng_min, lat_max, lng_max : float
tags : str or list of str, optional
    Node tags that will be used to filter the search.
    See http://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide
    for information about OSM Overpass queries
    and http://wiki.openstreetmap.org/wiki/Map_Features
    for a list of tags.
Returns
-------
nodes : pandas.DataFrame
    Will have 'lat' and 'lon' columns, plus other columns for the
    tags associated with the node (these will vary based on the query).
    Index will be the OSM node IDs.
File:      ~/mambaforge/envs/musa-550-fall-2023/lib/python3.10/site-packages/pandana/loaders/osm.py
Type:      function

Get the bounding box:

Show Code
boundary = center_city_outline.bounds
boundary, type(boundary)
((-75.1935824529863, 39.9286257450625, -75.1259307965394, 39.9738724134054),
 tuple)
Show Code
[lng_min, lat_min, lng_max, lat_max] = boundary
Show Code
# query OSM
poi_df = osm.node_query(lat_min, lng_min, lat_max, lng_max)

# remove missing data
poi_df = poi_df.dropna(subset=["amenity"])
Show Code
poi_df.head()
lat lon railway light_rail name network operator public_transport wheelchair highway ... hgv maxstay network:website payment:app:tesla ref:supercharge_info scooter socket:nacs socket:nacs:output diet:mediterranean restaurant
id
110237774 39.950401 -75.156620 NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
274216093 39.957640 -75.191254 NaN NaN Citibank NaN Citibank NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
274217596 39.953015 -75.192289 NaN NaN Starbucks NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
313440966 39.963935 -75.166939 NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
313440968 39.963978 -75.170407 NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 604 columns

Show Code
len(poi_df)
2897
Show Code
poi_df[["lat", "lon", "amenity"]].head(10)
lat lon amenity
id
110237774 39.950401 -75.156620 parking_entrance
274216093 39.957640 -75.191254 atm
274217596 39.953015 -75.192289 cafe
313440966 39.963935 -75.166939 car_sharing
313440968 39.963978 -75.170407 car_sharing
326519551 39.944261 -75.132616 ferry_terminal
333786044 39.940858 -75.158928 restaurant
333786095 39.941924 -75.157357 cafe
333786448 39.942642 -75.157958 bank
333786774 39.943102 -75.160030 pharmacy

5.1.1 Explore the amenities in this region

For the full list of amenities, see the OSM Wikipedia

Show Code
import altair as alt  # Import Altair first

chart = (
    alt.Chart(poi_df)
    .mark_bar()
    .encode(y=alt.Y("amenity", sort="-x"), x="count()", tooltip=["amenity", "count()"])
)

chart

5.2 Step 2: Create a Pandana network

  • Key function: pdna_network_from_bbox()
  • It takes a bounding box and returns the OSM network within that region.
  • Multiple network types: ‘walk’ and ‘drive’
Show Code
# !pip install osmnet
Show Code
net = osm.pdna_network_from_bbox(
    lat_min, lng_min, lat_max, lng_max, network_type="walk"
)
Requesting network data within bounding box from Overpass API in 1 request(s)
Posting to http://www.overpass-api.de/api/interpreter with timeout=180, "{'data': '[out:json][timeout:180];(way["highway"]["highway"!~"motor|proposed|construction|abandoned|platform|raceway"]["foot"!~"no"]["pedestrians"!~"no"](39.92862575,-75.19358245,39.97387241,-75.12593080);>;);out;'}"
Downloaded 8,805.4KB from www.overpass-api.de in 1.65 seconds
Downloaded OSM network data within bounding box from Overpass API in 1 request(s) and 1.75 seconds
Returning OSM data with 44,594 nodes and 14,392 ways...
Edge node pairs completed. Took 3.35 seconds
Returning processed graph with 21,080 nodes and 31,645 edges...
Completed OSM data download and Pandana node and edge table creation in 5.44 seconds
Generating contraction hierarchies with 12 threads.
Setting CH node vector of size 21080
Setting CH edge vector of size 31861
Range graph removed 974 edges of 63722
. 10% . 20% . 30% . 40% . 50% . 60% . 70% . 80% . 90% . 100%

5.3 Step 3: Tell the network the location of amenities

Key function: network.set_pois()

  • Today, we’ll explore these four amenities: “restaurant”, “bar”, “school”, “car_sharing”
  • IMPORTANT: if you want to explore other amenities, you’ll need to run the code below for your amenities of interest
Show Code
# sensible defaults
max_distance = 2000  # in meters
num_pois = 10  # only need the 10 nearest POI to each point in the network


AMENITIES = ["restaurant", "bar", "school", "car_sharing"]
for amenity in AMENITIES:
    
    # get the subset of amenities for this type
    pois_subset = poi_df[poi_df["amenity"] == amenity]

    # set the POI, using the longitude and latitude of POI
    net.set_pois(
        amenity, max_distance, num_pois, pois_subset["lon"], pois_subset["lat"]
    )
Show Code
# keyword arguments to pass for the matplotlib figure
bbox_aspect_ratio = (lat_max - lat_min) / (lng_max - lng_min)
fig_kwargs = {"facecolor": "w", "figsize": (10, 10 * bbox_aspect_ratio)}

# keyword arguments to pass for scatter plots
plot_kwargs = {"s": 20, "alpha": 0.9, "cmap": "viridis_r", "edgecolor": "none"}

5.4 Step 4: Plot the walking distance to the nearest POI

For every point on the network, find the nth nearest POI, calculate the distance, and color that point according to the distance.

  1. Use network.nearest_poi() to get distances from nodes to nearest POIs
  2. Merge coordinates of network nodes with distances to nearest POIs
  3. Plot the node coordinates, colored by distance to nth nearest POI

5.4.1 4.1 Use network.nearest_poi() to get distances from nodes to nearest POIs

Show Code
amenity = "bar"
access = net.nearest_pois(distance=2000, category=amenity, num_pois=num_pois)
Show Code
access.tail(n=50)
1 2 3 4 5 6 7 8 9 10
id
12992985501 387.933990 972.843018 985.156982 1002.348022 1002.487000 1004.830017 1074.785034 1199.498047 1208.161987 1241.420044
12992985502 388.527008 973.435974 985.750000 1002.940979 1003.080017 1005.422974 1075.378052 1200.090942 1208.755005 1242.012939
12992985505 385.356995 970.265991 982.580017 999.770996 999.909973 1002.252991 1072.208008 1196.921021 1205.584961 1238.843018
12992985506 447.894989 908.638000 920.952026 937.531006 938.143005 940.625000 1010.580017 1135.292969 1152.277954 1176.854004
12992985507 445.730988 909.205017 921.518982 938.710022 939.054016 941.192017 1011.146973 1135.859985 1150.114014 1177.781982
12992985510 321.196991 1026.510986 1033.896973 1044.662964 1063.401978 1063.745972 1065.884033 1135.838989 1164.211060 1262.045044
12992985513 360.200989 987.762024 995.598999 1005.914001 1025.104004 1026.192017 1027.586060 1097.541016 1125.462036 1264.176025
12992985514 364.709015 983.367004 991.091003 1001.518982 1020.596008 1023.078003 1023.276001 1093.032959 1121.067017 1259.667969
12992985515 408.295013 939.781006 947.505005 957.932983 977.010010 979.492004 980.556030 1049.447021 1077.480957 1216.082031
12992985519 489.779999 865.156006 877.469971 894.661011 897.143005 897.158997 967.098022 1091.811035 1106.897949 1133.733032
12992985520 442.889008 912.046997 924.361023 941.552002 941.895996 944.033997 1013.989014 1138.702026 1152.956055 1180.624023
12992985521 484.045013 870.890991 883.205017 900.395996 900.739990 902.877991 972.833008 1097.546021 1111.800049 1139.468018
12992985522 488.671997 866.263977 878.578003 895.768982 898.250977 898.267029 968.205994 1092.918945 1108.005981 1134.840942
12992985523 485.424988 869.510986 881.825012 899.015991 899.359985 901.497986 971.453003 1096.166016 1110.420044 1138.088013
12992985524 532.348999 822.586975 834.901001 852.091980 854.573975 855.638000 924.528992 1049.241943 1064.515015 1091.163940
12992985525 531.658020 824.002991 836.317017 853.507996 855.281006 855.989990 925.945007 1050.657959 1066.183960 1092.579956
12992985526 531.075989 823.859985 836.174011 853.364990 855.846985 855.862976 925.802002 1050.515015 1065.602051 1092.437012
12992985528 407.053986 941.021973 948.745972 959.174011 978.250977 980.732971 980.931030 1050.687988 1078.722046 1217.322998
12992985529 405.781006 939.807007 950.297974 957.958984 979.802979 982.284973 982.482971 1052.239990 1077.506958 1218.875000
12992985530 439.131989 916.754028 929.067993 946.258972 948.741028 948.853027 972.612000 1018.695984 1110.312012 1185.331055
12992985531 417.489014 930.586975 946.171021 948.739014 975.676025 978.158020 979.221985 1048.113037 1068.286987 1214.748047
12992985532 419.196014 926.992004 945.143982 948.182007 974.710022 980.169006 992.671021 1045.124023 1064.692017 1216.759033
12992985533 419.716003 927.512024 945.664001 948.702026 975.229980 980.689026 982.296997 1045.644043 1065.212036 1217.279053
12992985534 419.062012 925.750977 948.663025 951.700989 978.229004 983.687988 993.896973 1048.642944 1068.211060 1220.277954
12992985535 541.268005 813.668030 825.981995 843.172974 845.655029 854.213013 915.609985 1040.322998 1063.089966 1082.244995
12992985536 543.890991 811.044983 823.359009 840.549988 843.031982 853.656982 912.987000 1037.699951 1062.488037 1079.621948
12992985537 551.463013 811.017029 823.330994 840.521973 843.004028 846.085022 912.958984 1037.671997 1062.459961 1079.593994
12992985538 551.359009 813.750000 826.064026 843.255005 843.351990 845.737000 915.692017 1040.405029 1065.192993 1081.322021
12992985539 542.843018 819.911987 834.580017 844.096008 851.770996 851.898987 924.208008 1048.921021 1073.708984 1082.066040
12992985540 540.372986 822.382019 835.911987 846.565979 853.103027 854.369019 925.539978 1050.253052 1074.899048 1084.536011
12992985541 434.489990 923.127014 935.440979 951.823975 952.632019 955.114014 1025.068970 1149.781982 1164.876953 1191.343018
12992985543 441.598999 921.200989 935.310974 943.668030 952.502014 953.187988 1024.938965 1149.651978 1173.032959 1183.354980
12992985544 447.665009 915.135010 932.189026 937.601990 947.122009 949.380005 1021.817017 1146.530029 1170.741943 1177.288940
12992985545 456.290009 911.250000 923.564026 938.382996 940.755005 943.237000 1013.192017 1137.905029 1162.116943 1178.069946
12992985546 455.968994 908.638000 920.952026 937.531006 938.143005 940.625000 1010.580017 1135.292969 1159.505005 1176.854004
12992985551 452.804993 935.179993 1023.153015 1230.785034 1233.822998 1260.350952 1265.810059 1267.124023 1330.765015 1350.333008
13006788998 127.803001 152.214996 294.419006 391.424988 490.063995 978.643005 1295.319946 1295.319946 1679.610962 1729.196045
13006788999 216.914001 241.326004 383.529999 480.536011 579.174988 1067.754028 1384.431030 1384.431030 1768.722046 1818.307007
13006834609 56.481998 80.893997 223.098007 320.104004 418.743011 907.322021 1223.999023 1223.999023 1608.290039 1657.875000
13014037121 21.089001 120.253998 124.250000 153.820999 571.645020 588.414001 589.476013 605.718994 631.114990 842.487976
13014037122 42.951000 142.115997 146.112000 175.682999 549.783020 566.552002 567.614014 583.856995 652.976990 820.625977
13014037124 50.694000 153.854996 166.490005 183.425995 525.408997 542.177979 543.239990 559.482971 660.719971 796.252014
13025713803 1238.119019 1334.927979 1768.760010 1916.592041 1969.269043 1975.082031 1975.082031 1984.400024 2000.000000 2000.000000
13025955606 436.227997 700.471008 755.395020 755.395020 1200.781982 1200.781982 1200.781982 1208.651978 1210.306030 1210.306030
13025955607 429.696014 707.002991 761.927002 761.927002 1202.119995 1207.313965 1207.313965 1207.313965 1216.838013 1216.838013
13025955608 439.209991 699.843994 754.768005 754.768005 1200.155029 1200.155029 1200.155029 1209.678955 1209.678955 1220.390991
13025955609 433.653992 694.288025 749.211975 749.211975 1194.598999 1194.598999 1194.598999 1204.123047 1204.123047 1214.834961
13025955610 431.411011 708.330017 763.254028 763.254028 1198.113037 1198.113037 1198.113037 1207.636963 1207.636963 1213.350952
13025955611 437.908997 698.543030 753.466980 753.466980 1198.854004 1198.854004 1198.854004 1208.378052 1208.378052 1210.579956
13025955612 437.546997 702.193970 757.117981 757.117981 1202.505005 1202.505005 1202.505005 1212.029053 1212.029053 1219.487061

5.4.2 4.2 Merge coordinates of network nodes with distances to nearest POIs

Show Code
net.nodes_df.head()
x y
id
103327043 -75.128666 39.934488
103332711 -75.128062 39.939224
103332730 -75.129329 39.941869
103333406 -75.130257 39.943351
103333412 -75.130332 39.943123
Show Code
access.head()
1 2 3 4 5 6 7 8 9 10
id
103327043 1752.093018 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0
103332711 988.283020 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0
103332730 670.846008 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0
103333406 597.692993 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0
103333412 623.888977 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0
Show Code
import pandas as pd

# Merge the nodes and the distance to POIs
nodes = pd.merge(net.nodes_df, access, left_index=True, right_index=True)

# Make into a geodataframe
nodes = gpd.GeoDataFrame(
    nodes, geometry=gpd.points_from_xy(nodes["x"], nodes["y"]), crs="EPSG:4326"
)
Show Code
nodes.head()
x y 1 2 3 4 5 6 7 8 9 10 geometry
id
103327043 -75.128666 39.934488 1752.093018 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 POINT (-75.12867 39.93449)
103332711 -75.128062 39.939224 988.283020 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 POINT (-75.12806 39.93922)
103332730 -75.129329 39.941869 670.846008 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 POINT (-75.12933 39.94187)
103333406 -75.130257 39.943351 597.692993 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 POINT (-75.13026 39.94335)
103333412 -75.130332 39.943123 623.888977 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 2000.0 POINT (-75.13033 39.94312)

5.4.3 4.3 And now plot it!

Let’s define a function to do this for us, since we’ll repeat this plot multiple times

Show Code
def plot_walking_distance(net, amenity, distance=1000, n=1):
    """
    Plot the walking distance to the specified amenity
    """
    from mpl_toolkits.axes_grid1 import make_axes_locatable

    # subset of POI
    poi_subset = poi_df[poi_df["amenity"] == amenity]

    # get the distances to nearest num_pois POI
    access = net.nearest_pois(distance=1000, category=amenity, num_pois=num_pois)

    # merge node positions and distances to nearest PO
    nodes = pd.merge(net.nodes_df, access, left_index=True, right_index=True)
    nodes = gpd.GeoDataFrame(
        nodes, geometry=gpd.points_from_xy(nodes["x"], nodes["y"]), crs="EPSG:4326"
    )

    # Create the figure
    fig, ax = plt.subplots(figsize=(10, 10))
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.1)

    # plot the distance to the nth nearest amenity
    ax = nodes.plot(ax=ax, cax=cax, column=nodes[n], legend=True, **plot_kwargs)

    # add the amenities as stars
    for i, row in poi_subset.iterrows():
        ax.scatter(row["lon"], row["lat"], color="red", s=100, marker="*")

    # format
    ax.set_facecolor("black")
    ax.figure.set_size_inches(fig_kwargs["figsize"])

    # set extent
    [xmin, ymin, xmax, ymax] = nodes.geometry.total_bounds
    ax.set_xlim(xmin, xmax)
    ax.set_ylim(ymin, ymax)

    return ax

5.5 Evaluating amenity choice

The difference between maps to the nearest amenity and for example, the 5th nearest amenity tells us about the options consumers have

5.5.1 Example: bars

Show Code
ax = plot_walking_distance(net, "bar", n=1)
ax.set_title("Walking distance to the nearest bar", fontsize=18);

Show Code
ax = plot_walking_distance(net, "bar", n=3)
ax.set_title("Walking distance to the 3rd nearest bar", fontsize=18);

5.5.2 Example: schools

Show Code
ax = plot_walking_distance(net, "school", n=1)
ax.set_title("Walking distance to the nearest school", fontsize=18);

Show Code
ax = plot_walking_distance(net, "school", n=3)
ax.set_title("Walking distance to the 3rd nearest school", fontsize=18);

5.5.3 Example: restaurants

Show Code
ax = plot_walking_distance(net, "restaurant", n=1)
ax.set_title("Walking distance to the nearest restaurant", fontsize=18);

Show Code
ax = plot_walking_distance(net, "restaurant", n=10)
ax.set_title("Walking distance to the 10th nearest restaurant", fontsize=18);

5.5.4 Example: car sharing

Show Code
ax = plot_walking_distance(net, "car_sharing", n=1)
ax.set_title("Walking distance to the nearest car sharing", fontsize=18);

Show Code
ax = plot_walking_distance(net, "car_sharing", n=10)
ax.set_title("Walking distance to the 10th nearest car sharing", fontsize=18);

6 At-home exercise: Explore amenities in the neighborhood of your choice

Many, many more amenities are logged throughout the city. Pick your favorite neighborhood and explore.

See this page for the full list of amenities.

Final project idea: With this kind of analysis, you can look at amenity-based influence in housing, neighborhood selection, etc. or something similar to the Walk Score.